/**
 * Created by xujian1 on 2018/10/8.
 */

import Event from 'events'
import { getCurrentScreen } from './utils.js'

const CREATE_RECT = 1
const MOVING_RECT = 2
const RESIZE = 3

const ANCHORS = [
  { row: 'x', col: 'y', cursor: 'nwse-resize' },
  { row: '', col: 'y', cursor: 'ns-resize' },
  { row: 'r', col: 'y', cursor: 'nesw-resize' },

  { row: 'x', col: '', cursor: 'ew-resize' },
  { row: 'r', col: '', cursor: 'ew-resize' },

  { row: 'x', col: 'b', cursor: 'nesw-resize' },
  { row: '', col: 'b', cursor: 'ns-resize' },
  { row: 'r', col: 'b', cursor: 'nwse-resize' }
]

export class CaptureEditor extends Event {
  constructor($canvas) {
    super()
    this.$canvas = $canvas
    this.disabled = false
    let currentScreen = getCurrentScreen()
    this.scaleFactor = currentScreen.scaleFactor
    this.screenWidth = currentScreen.bounds.width
    this.screenHeight = currentScreen.bounds.height
    this.ctx = $canvas.getContext('2d')

    this.onMouseDown = this.onMouseDown.bind(this)
    this.onMouseMove = this.onMouseMove.bind(this)
    this.onMouseUp = this.onMouseUp.bind(this)

    this.addEvent()
  }

  addEvent() {
    document.addEventListener('mousedown', this.onMouseDown)
    document.addEventListener('mousemove', this.onMouseMove)
    document.addEventListener('mouseup', this.onMouseUp)
  }

  removeEvent() {
    document.removeEventListener('mousedown', this.onMouseDown)
    document.removeEventListener('mousemove', this.onMouseMove)
    document.removeEventListener('mouseup', this.onMouseUp)
  }

  onMouseDown(e) {
    if (this.disabled) {
      return
    }
    this.mouseDown = true
    const { pageX, pageY } = e
    if (this.selectRect) {
      const { w, h, x, y, r, b } = this.selectRect
      if (this.selectAnchorIndex !== -1) {
        this.startPoint = {
          x: pageX,
          y: pageY,
          moved: false,
          selectRect: {
            w,
            h,
            x,
            y,
            r,
            b
          },
          rawRect: {
            w,
            h,
            x,
            y,
            r,
            b
          }
        }
        this.action = RESIZE
        return
      }
      this.startPoint = {
        x: e.pageX,
        y: e.pageY,
        moved: false
      }
      if (pageX > x && pageX < r && pageY > y && pageY < b) {
        this.action = MOVING_RECT
        this.startDragRect = {
          x: pageX,
          y: pageY,
          selectRect: {
            x,
            y,
            w,
            h,
            r,
            b
          }
        }
      } else {
        this.action = CREATE_RECT
      }
    } else {
      this.action = CREATE_RECT
      this.startPoint = {
        x: e.pageX,
        y: e.pageY,
        moved: false
      }
      e.stopPropagation()
      e.preventDefault()
    }
  }

  onMouseDrag(e) {
    if (this.disabled) {
      return
    }
    e.stopPropagation()
    e.preventDefault()

    const { pageX, pageY } = e
    let startDragging
    let selectRect = this.selectRect
    if (!this.startPoint.moved) {
      if (Math.abs(this.startPoint.x - pageX) > 10 || Math.abs(this.startPoint.y - pageY) > 10) {
        this.startPoint.moved = true
        startDragging = true
      }
    }
    if (!this.startPoint.moved) {
      return
    }

    if (this.action === MOVING_RECT) {
      // 移动选区
      if (startDragging) {
        this.emit('start-dragging', selectRect)
      }
      this.emit('dragging', selectRect)
      const { w, h } = selectRect
      const { x: startX, y: startY } = this.startPoint
      let newX = this.startDragRect.selectRect.x + pageX - startX
      let newY = this.startDragRect.selectRect.y + pageY - startY
      let newR = newX + w
      let newB = newY + h
      if (newX < 0) {
        newX = 0
        newR = w
      } else if (newR > this.screenWidth) {
        newR = this.screenWidth
        newX = newR - w
      }
      if (newY < 0) {
        newY = 0
        newB = h
      } else if (newB > this.screenHeight) {
        newB = this.screenHeight
        newY = newB - h
      }
      this.selectRect = {
        w,
        h,
        x: newX,
        y: newY,
        r: newR,
        b: newB
      }
      this.drawRect()
    } else if (this.action === RESIZE) {
      this.emit('dragging', selectRect)
      let { row, col } = ANCHORS[this.selectAnchorIndex]
      if (row) {
        this.startPoint.rawRect[row] = this.startPoint.selectRect[row] + pageX - this.startPoint.x
        selectRect.x = this.startPoint.rawRect.x
        selectRect.r = this.startPoint.rawRect.r
        if (selectRect.x > selectRect.r) {
          let x = selectRect.r
          selectRect.r = selectRect.x
          selectRect.x = x
        }
        selectRect.w = selectRect.r - selectRect.x
        this.startPoint.rawRect.w = selectRect.w
      }
      if (col) {
        this.startPoint.rawRect[col] = this.startPoint.selectRect[col] + pageY - this.startPoint.y
        selectRect.y = this.startPoint.rawRect.y
        selectRect.b = this.startPoint.rawRect.b

        if (selectRect.y > selectRect.b) {
          let y = selectRect.b
          selectRect.b = selectRect.y
          selectRect.y = y
        }
        selectRect.h = selectRect.b - selectRect.y
        this.startPoint.rawRect.h = selectRect.h
      }
      this.drawRect()
    } else {
      // 生成选区
      const { pageX, pageY } = e
      let x, y, w, h, r, b
      if (this.startPoint.x > pageX) {
        x = pageX
        r = this.startPoint.x
      } else {
        r = pageX
        x = this.startPoint.x
      }
      if (this.startPoint.y > pageY) {
        y = pageY
        b = this.startPoint.y
      } else {
        b = pageY
        y = this.startPoint.y
      }
      w = r - x
      h = b - y

      this.selectRect = {
        x,
        y,
        w,
        h,
        r,
        b
      }
      selectRect = this.selectRect
      if (startDragging) {
        this.emit('start-dragging', selectRect)
      }
      this.emit('dragging', selectRect)
      this.drawRect(x, y, w, h)
    }
  }

  drawRect() {
    if (this.disabled) {
      return
    }
    if (!this.selectRect) {
      this.$canvas.style.display = 'none'
      return
    }
    const { x, y, w, h } = this.selectRect

    const scaleFactor = this.scaleFactor
    let margin = 7
    let radius = 5
    this.$canvas.style.left = `${x - margin}px`
    this.$canvas.style.top = `${y - margin}px`
    this.$canvas.style.width = `${w + margin * 2}px`
    this.$canvas.style.height = `${h + margin * 2}px`
    this.$canvas.style.display = 'block'
    this.$canvas.width = (w + margin * 2) * scaleFactor
    this.$canvas.height = (h + margin * 2) * scaleFactor

    this.ctx.fillStyle = '#ffffff'
    this.ctx.strokeStyle = '#67bade'
    this.ctx.lineWidth = 2 * this.scaleFactor

    this.ctx.strokeRect(
      margin * scaleFactor,
      margin * scaleFactor,
      w * scaleFactor,
      h * scaleFactor
    )
    this.drawAnchors(w, h, margin, scaleFactor, radius)
  }

  drawAnchors(w, h, margin, scaleFactor, radius) {
    if (this.disabled) {
      return
    }
    if (this.mouseDown && this.action === CREATE_RECT) {
      this.anchors = null
      return
    }
    this.ctx.beginPath()
    let anchors = [
      [0, 0],
      [(w * this.scaleFactor) / 2, 0],
      [w * this.scaleFactor, 0],

      [0, (h * this.scaleFactor) / 2],
      [w * this.scaleFactor, (h * this.scaleFactor) / 2],

      [0, h * this.scaleFactor],
      [(w * this.scaleFactor) / 2, h * this.scaleFactor],
      [w * this.scaleFactor, h * this.scaleFactor]
    ]
    this.anchors = anchors.map(([x, y]) => [
      this.selectRect.x + x / scaleFactor,
      this.selectRect.y + y / scaleFactor
    ])
    anchors.forEach(([x, y], i) => {
      this.ctx.arc(
        x + margin * scaleFactor,
        y + margin * scaleFactor,
        radius * scaleFactor,
        0,
        2 * Math.PI
      )
      let next = anchors[(i + 1) % anchors.length]
      this.ctx.moveTo(
        next[0] + margin * scaleFactor + radius * scaleFactor,
        next[1] + margin * scaleFactor
      )
    })
    this.ctx.closePath()
    this.ctx.fill()
    this.ctx.stroke()
  }

  onMouseMove(e) {
    if (this.disabled) {
      return
    }
    if (this.mouseDown) {
      this.onMouseDrag(e)
      return
    }
    this.selectAnchorIndex = -1
    if (this.selectRect) {
      const { pageX, pageY } = e
      const { x, y, r, b } = this.selectRect
      let selectAnchor,
        selectIndex = -1
      if (this.anchors) {
        this.anchors.forEach(([x, y], i) => {
          if (Math.abs(pageX - x) <= 10 && Math.abs(pageY - y) <= 10) {
            selectAnchor = [x, y]
            selectIndex = i
          }
        })
      }
      if (selectAnchor) {
        this.selectAnchorIndex = selectIndex
        document.body.style.cursor = ANCHORS[selectIndex].cursor
        this.emit('moving')
        return
      }
      if (pageX > x && pageX < r && pageY > y && pageY < b) {
        document.body.style.cursor = 'move'
      } else {
        document.body.style.cursor = 'auto'
      }
      this.emit('moving')
    }
  }

  onMouseUp(e) {
    if (this.disabled) {
      return
    }
    if (!this.mouseDown) {
      return
    }
    this.mouseDown = false
    e.stopPropagation()
    e.preventDefault()
    this.emit('mouse-up')
    if (!this.startPoint.moved) {
      this.emit('end-moving')
      return
    }
    this.emit('end-dragging')
    this.drawRect()
    this.startPoint = null
  }

  disable() {
    this.disabled = true
  }

  enable() {
    this.disabled = false
  }

  reset() {
    this.anchors = null
    this.startPoint = null
    this.selectRect = null
    this.startDragRect = null
    this.selectAnchorIndex = -1
    this.drawRect()
    this.emit('reset')
  }
}
